package handler;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

/**
 * Provides methods to read and write notebook equations to/from XML. This class
 * is a refactoring of the original Math Graph project. It was refactored to
 * operate on input and output streams, rather than on files. This allows
 * for unit testing and the ability to chain encryption, compression, and xml
 * parsing. The caller is resonsible for closing any provided input or
 * output stream.
 */
public class XMLHandler {
    /**
     * Parses the input stream and returns a list of notebook equations
     * 
     * @param inputStream stream from which to read the XML
     * @return List of EquateNode objects
     */
    public List<EquateNode> getEquations(final InputStream inputStream) {
        List<EquateNode> ntbk = new ArrayList<EquateNode>();

        // import all data from the stream and save in data structure
        try {
            final XMLInputFactory factory = XMLInputFactory.newInstance();
            final XMLStreamReader reader = factory.createXMLStreamReader(inputStream);
            int equationCounter = -1;
            IntervalNode inNode;
            String a = "";
            String b = "";
            ArrayList<Double> tanPoints = new ArrayList<Double>();

            // Iterate over the XML document
            while (reader.hasNext()) {
                int eventType = reader.getEventType();
                if (eventType == XMLStreamConstants.START_ELEMENT && "equation".equals(reader.getLocalName())) {
                    reader.next(); // advance to the contents of equation tag
                    ntbk.add(new EquateNode(reader.getText()));
                    equationCounter++;

                    reader.next();
                    eventType = reader.getEventType();
                    while (!(eventType == XMLStreamConstants.END_ELEMENT && "page".equals(reader.getLocalName()))) {
                        if (eventType == XMLStreamConstants.START_ELEMENT && "interval".equals(reader.getLocalName())) {

                            while (!(eventType == XMLStreamConstants.END_ELEMENT && "interval".equals(reader.getLocalName()))) {
                                // gather interval info
                                if (eventType == XMLStreamConstants.START_ELEMENT && "a".equals(reader.getLocalName())) {
                                    reader.next();
                                    a = reader.getText();
                                } else if (eventType == XMLStreamConstants.START_ELEMENT && "b".equals(reader.getLocalName())) {
                                    reader.next();
                                    b = reader.getText();
                                } else if (eventType == XMLStreamConstants.START_ELEMENT && "tanPt".equals(reader.getLocalName())) {
                                    reader.next();
                                    tanPoints.add(Double.parseDouble(reader.getText()));
                                }

                                reader.next();
                                eventType = reader.getEventType();
                            }

                            if (!a.equals("") && !b.equals("")) {
                                // add the interval information gathered from
                                // above while loop
                                inNode = new IntervalNode(Double.parseDouble(a), Double.parseDouble(b));
                                for (int g = 0; g < tanPoints.size(); g++) {
                                    inNode.addTanPt(tanPoints.get(g).doubleValue());
                                }
                                ntbk.get(equationCounter).addInterval(inNode);

                                // reset tanPoints array for possible next
                                // iteration
                                tanPoints = new ArrayList<Double>();
                            }

                        }

                        reader.next();
                        eventType = reader.getEventType();
                    }
                }

                reader.next();
            }

        } catch (final Exception e) {
            System.err.println("Error occured in XMLHandler(): " + e);
        }

        return ntbk;
    }

    /**
     * Saves the specified notebook equations to the output stream in XML form
     * 
     * @param notebookEquations a collection of equations that should be saved to the specified output stream
     * @param outputStream the xml generated by this method will be written to this output stream
     */
    public void saveEquations(List<EquateNode> notebookEquations, OutputStream outputStream) {
        EquateNode enode;
        ArrayList<IntervalNode> inodeAry;
        IntervalNode inode;
        ArrayList<Double> tanPtAry;
        Double tanPt;

        try {
            final XMLOutputFactory factory = XMLOutputFactory.newInstance();
            final XMLStreamWriter writer = factory.createXMLStreamWriter(outputStream);

            writer.writeStartDocument("1.0"); // Write the start document (XML
                                              // version 1.0)
            writer.writeStartElement("mathnotebook");

            for (int i = 0; i < notebookEquations.size(); i++) {
                enode = notebookEquations.get(i);

                writer.writeStartElement("page");

                writer.writeStartElement("equation");
                writer.writeCharacters(enode.getEquation());
                writer.writeEndElement();

                inodeAry = enode.getIntervals();
                for (int j = 0; j < inodeAry.size(); j++) {
                    inode = inodeAry.get(j);
                    writer.writeStartElement("interval");

                    writer.writeStartElement("a");
                    writer.writeCharacters(inode.getA() + "");
                    writer.writeEndElement();
                    writer.writeStartElement("b");
                    writer.writeCharacters(inode.getB() + "");
                    writer.writeEndElement();

                    tanPtAry = inode.getTanPts();
                    for (int k = 0; k < tanPtAry.size(); k++) {
                        tanPt = tanPtAry.get(k);

                        writer.writeStartElement("tanPt");
                        writer.writeCharacters(tanPt.doubleValue() + "");
                        writer.writeEndElement();
                    }

                    writer.writeEndElement();
                }

                writer.writeEndElement();
            }

            writer.writeEndElement();
            writer.writeEndDocument();
            writer.flush();
            writer.close();

        } catch (final Exception e) {
            System.err.println("Error occured in XMLHandler.saveFile(): " + e);
        }
    }
}
